#pragma once

#include "Util.hpp"
#include "Log.hpp"

#include <string>
#include <vector>
#include <unordered_map>
#include <unistd.h>
#include <sstream>
#include <algorithm>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define INTERNAL_SERVER_ERROR 500

#define LINE_END "\r\n"
#define SEP ": "

#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "Http/1.0"
#define PAGE_400 "400.html"
#define PAGE_404 "404.html"
#define PAGE_500 "500.html"

static std::string Code2Desc(int code)
{
    std::string desc;
    switch (code)
    {
    case 200:
        desc = "OK";
        break;
    case 400:
        desc = "Bad Request";
        break;
    case 404:
        desc = "Not Found";
        break;
    case 500:
        desc = "Internal Server Error";
        break;
    default:
        break;
    }
    return desc;
}

static std::string Suffix2Desc(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc{
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"}};

    auto iter = suffix2desc.find(suffix);
    if (iter != suffix2desc.end())
    {
        return iter->second;
    }
    return "text/html";
}

class HttpRequest
{
public:
    // HTTP请求内容
    std::string _request_line;
    std::vector<std::string> _request_header;
    std::string _blank;
    std::string _request_body;

    // 解析结果
    std::string _method;
    std::string _uri;
    std::string _version;
    std::unordered_map<std::string, std::string> _header_kv;
    int _content_length;
    std::string _path;
    std::string _query_string;

    // CGI
    bool _cgi;

public:
    HttpRequest()
        : _content_length(0), _cgi(false)
    {
    }
    ~HttpRequest()
    {
    }
};

class HttpResponse
{
public:
    // HTTP响应内容
    std::string _status_line;
    std::vector<std::string> _reponse_header;
    std::string _blank;
    std::string _reponse_body; // 响应正文（CGI）

    // 所需数据
    int _status_code;    // 状态码
    int _fd;             // 响应文件的fd  （非CGI）
    int _size;           // 响应文件的大小（非CGI）
    std::string _suffix; // 响应文件的后缀（非CGI）

public:
    HttpResponse()
        : _blank(LINE_END), _status_code(OK), _fd(-1), _size(0)
    {
    }
    ~HttpResponse()
    {
    }
};

class EndPoint
{
private:
    int _sock;
    HttpRequest _http_request;
    HttpResponse _http_response;
    bool _stop;

public:
    EndPoint(int sock)
        : _sock(sock), _stop(false)
    {
    }
    ~EndPoint()
    {
    }

    bool IsStop()
    {
        return _stop;
    }

    // 读取请求
    void RecvHttpRequest()
    {
        if (!RecvHttpRequestLine() && !RecvHttpRequestHeader()) // 读取请求行 报头和空行
        {
            ParseHttpRequestLine();   // 解析请求行
            ParseHttpRequestHeader(); // 解析请求报头
            RecvHttpRequestBody();    // 读取请求正文
        }
    }
    // 处理请求
    void HandlerHttpRequest()
    {
        auto &code = _http_response._status_code;
        if (_http_request._method != "GET" && _http_request._method != "POST")
        {
            LOG(WARNING, "method is wrong");
            code = BAD_REQUEST;
            return;
        }
        if (_http_request._method == "GET")
        {
            size_t pos = _http_request._uri.find("?");
            if (pos != _http_request._uri.npos)
            {
                Util::CurString(_http_request._uri, _http_request._path, _http_request._query_string, "?");
                _http_request._cgi = true;
            }
            else
            {
                _http_request._path = _http_request._uri;
            }
        }
        else if (_http_request._method == "POST")
        {
            _http_request._path = _http_request._uri;
            _http_request._cgi = true;
        }
        else
        {
        }

        // 给请求资源路径拼接web根目录
        std::string path = _http_request._path;
        _http_request._path = WEB_ROOT;
        _http_request._path += path;
        // 请求资源路径以/结尾，说明请求的是一个目录
        if (_http_request._path[_http_request._path.size() - 1] == '/')
        {
            _http_request._path += HOME_PAGE;
        }

        // 获取请求资源文件的属性信息
        struct stat st;
        // 属性信息获取成功，说明该资源存在
        if (stat(_http_request._path.c_str(), &st) == 0)
        {
            if (S_ISDIR(st.st_mode)) // 该资源是一个目录
            {
                _http_request._path += "/";
                _http_request._path += HOME_PAGE;
                stat(_http_request._path.c_str(), &st);
            }
            else if (st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH) // 该资源是一个可执行程序
            {
                _http_request._cgi = true;
            }
            _http_response._size = st.st_size;
        }
        else // 属性信息获取失败，可以认为该资源不存在
        {
            LOG(WARNING, _http_request._path + " NOT_FOUND");
            code = NOT_FOUND;
            return;
        }

        // 获取请求资源文件的后缀
        size_t pos = _http_request._path.rfind('.');
        if (pos != _http_request._path.npos)
        {
            _http_response._suffix = _http_request._path.substr(pos);
        }
        else
        {
            _http_response._suffix = ".html";
        }
        // 进行CGI或非CGI处理
        if (_http_request._cgi)
            code = ProcessCgi(); // 以CGI的方式进行处理
        else
            code = ProcessNoCgi(); // 返回网页
    }
    // 构建响应
    void BuildHttpRequest()
    {
        int code = _http_response._status_code;
        auto &status_line = _http_response._status_line;
        // 状态行
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += Code2Desc(code);
        status_line += LINE_END;
        // 响应报头
        std::string path = WEB_ROOT;
        path += "/";
        switch (code)
        {
        case OK:
            BuildOkResponse();
            break;
        case BAD_REQUEST:
            path += PAGE_400;
            HandlerError(path);
            break;
        case NOT_FOUND:
            path += PAGE_404;
            HandlerError(path);
            break;
        case INTERNAL_SERVER_ERROR:
            path += PAGE_500;
            HandlerError(path);
            break;
        default:
            break;
        }
    }
    // 发送响应
    bool SendHttpRequest()
    {
        // 发送状态行
        if (send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0) <= 0)
            _stop = true;
        // 发送响应报头
        if (!IsStop())
        {
            for (auto &it : _http_response._reponse_header)
            {
                if (send(_sock, it.c_str(), it.size(), 0) <= 0)
                {
                    _stop = true;
                    break;
                }
            }
        }
        // 发送空行
        if (!IsStop())
        {
            if (send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0) <= 0)
                _stop = true;
        }
        // 发送响应正文
        if (_http_request._cgi)
        {
            if (!IsStop())
            {
                auto &response_body = _http_response._reponse_body;
                const char *start = response_body.c_str();
                int total = 0;
                int size = 0;
                while (total < response_body.size() && (size = send(_sock, start + total, response_body.size() - total, 0)))
                {
                    total += size;
                }
            }
        }
        else
        {
            if (!IsStop())
            {
                if (sendfile(_sock, _http_response._fd, nullptr, _http_response._size) <= 0)
                    _stop = true;
            }
        }
        // 关闭请求的资源文件
        close(_http_response._fd);
        return _stop;
    }

private:
    void BuildOkResponse()
    {
        // 构建响应报头
        std::string content_type = "Content_Type: ";
        content_type += Suffix2Desc(_http_response._suffix);
        content_type += LINE_END;
        _http_response._reponse_header.push_back(content_type);

        std::string content_length = "Content_Length: ";
        if (_http_request._cgi)
        { // 以CGI方式请求
            content_length += std::to_string(_http_response._reponse_body.size());
        }
        else
        { // 以非CGI方式请求
            content_length += std::to_string(_http_response._size);
        }
        content_length += LINE_END;
        _http_response._reponse_header.push_back(content_length);
    }

    void HandlerError(const std::string &path)
    {
        _http_request._cgi = false;
        // 打开对应的错误页面文件，以供后续发送
        _http_response._fd = open(path.c_str(), O_RDONLY);
        if (_http_response._fd > 0) // 打开文件成功
        {                           // 构建响应报头
            struct stat st;
            stat(path.c_str(), &st); // 获取错误页面文件的属性信息

            std::string content_type = "Content_Type: text/html";
            content_type += LINE_END;
            _http_response._reponse_header.push_back(content_type);

            std::string content_length = "Content_Length: ";
            content_length += std::to_string(st.st_size);
            content_length += LINE_END;
            _http_response._reponse_header.push_back(content_length);

            _http_response._size = st.st_size;
        }
    }

    int ProcessCgi()
    {
        int code = OK;
        auto &bin = _http_request._path;

        // 需要传递给CGI程序的参数
        auto &query_string = _http_request._query_string; // GET
        auto &request_body = _http_request._request_body; // POST
        auto &method = _http_request._method;
        int content_length = _http_request._content_length; // 请求正文的长度
        auto &response_body = _http_response._reponse_body; // CGI程序的处理结果放到响应正文当中
        // 1、创建两个匿名管道（管道命名站在父进程角度）
        // 创建从子进程到父进程的通信信道
        int input[2];
        if (pipe(input) < 0) // 管道创建失败，则返回对应的状态码
        {
            LOG(ERROR, "pipe input error!");
            code = INTERNAL_SERVER_ERROR;
            return code;
        }

        int output[2];
        if (pipe(output) < 0) // 管道创建失败，则返回对应的状态码
        {
            LOG(ERROR, "pipe output error!");
            code = INTERNAL_SERVER_ERROR;
            return code;
        }
        // 2、创建子进程
        pid_t pid = fork();
        if (pid == 0)
        { // child
            close(input[0]);
            close(output[1]);

            // 将请求方法通过环境变量传参

            std::string method_env = "METHOD=";
            method_env += method;
            putenv((char *)method_env.c_str());

            if (method == "GET")
            {
                std::string query_env = "QUERY_STRING=";
                query_env += _http_request._query_string;
                putenv((char *)query_env.c_str());
                LOG(INFO, "GET method,putenv query_string success");
            }
            else if (method == "POST")
            {
                std::string content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(_http_request._content_length);
                putenv((char *)content_length_env.c_str());
                LOG(INFO, "POST method,putenv content_length success");
            }
            else
            {
            }
            // 3、将子进程的标准输入输出进行重定向
            dup2(input[1], 1);
            dup2(output[0], 0);

            // 4、将子进程替换为对应的CGI程序
            execl(bin.c_str(), bin.c_str(), nullptr);
            // 执行到此处，替换失败
            exit(1);
        }
        else
        { // parent
            close(input[1]);
            close(output[0]);

            if (method == "POST")
            { // 将正文中的参数通过管道传递给CGI程序
                const char *start = request_body.c_str();
                int total = 0;
                int size = 0;
                while (total < content_length && (size = write(output[1], start + total, request_body.size() - total))>0)
                {
                    total += size;
                }
            }
            char ch = 0;
            while (read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
            } // 当另一端关闭后会继续执行下面的代码

            // 读取CGI程序的处理结果
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if (ret == pid)
            {
                if (WIFEXITED(status)) // 正常退出
                {
                    if (WEXITSTATUS(status) == 0)
                    {
                        LOG(INFO, "CGI program exits normally with correct results");
                        code = OK;
                    }
                    else
                    {
                        LOG(INFO, "CGI program exits normally with incorrect results");
                        code = BAD_REQUEST;
                    }
                }
                else
                {
                    LOG(INFO, "CGI program exits abnormally");
                    code = INTERNAL_SERVER_ERROR;
                }
            }
            // 关闭两个管道对应的文件描述符
            close(input[0]);
            close(output[1]);
        }
        return code;
    }

    int ProcessNoCgi()
    {
        // 打开客户端请求的资源文件，以供后续发送
        _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
        if (_http_response._fd >= 0)
            return OK;
        else
            return INTERNAL_SERVER_ERROR;
    }

    bool RecvHttpRequestLine()
    {
        auto &line = _http_request._request_line;
        if (Util::ReadLine(_sock, line) > 0)
        {
            line.resize(line.size() - 1);
        }
        else // 读取出错，则停止本次处理
        {
            _stop = true;
        }
        return _stop;
    }

    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (true)
        {
            line.clear();
            if (Util::ReadLine(_sock, line) <= 0) // 读取出错
            {
                _stop = true;
                break;
            }
            if (line == "\n")
            {
                _http_request._blank = line;
                break;
            }
            line.resize(line.size() - 1);
            _http_request._request_header.push_back(line);
        }
        return _stop;
    }

    void ParseHttpRequestLine()
    {
        auto &line = _http_request._request_line;

        // 通过stringstream拆分请求行
        std::stringstream ss(line);
        ss >> _http_request._method >> _http_request._uri >> _http_request._version;

        // 将请求方法统一转换为全大写
        auto &method = _http_request._method;
        std::transform(method.begin(), method.end(), method.begin(), toupper);
    }

    void ParseHttpRequestHeader()
    {
        std::string key, value;
        for (auto &it : _http_request._request_header)
        {
            if (Util::CurString(it, key, value, SEP))
            {
                _http_request._header_kv.insert({key, value});
            }
        }
    }

    bool IsNeedRecvHttpRequestBody()
    {
        auto &body = _http_request._request_body;
        if (_http_request._method == "POST")
        { // 请求方法为POST则需要读取正文
            auto &header_kv = _http_request._header_kv;
            auto iter = header_kv.find("Content-Length");
            if (iter != header_kv.end())
            {
                _http_request._content_length = atoi(iter->second.c_str());
                return true;
            }
        }
        else
            return false;
    }

    bool RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int content_length = _http_request._content_length;
            auto &body = _http_request._request_body;
            char ch;
            while (content_length--)
            {
                ssize_t size = recv(_sock, &ch, 1, 0);
                if (size > 0)
                {
                    body.push_back(ch);
                }
                else // 读取出错或对端关闭
                {
                    _stop = true;
                    break;
                }
            }
            return _stop;
        }
    }
};

class CallBack
{
public:
    void operator()(int sock)
    {
        HandlerHttpRequest(sock);
    }
    void HandlerHttpRequest(int sock)
    {
        LOG(INFO, "handler request begin");
        EndPoint *ep = new EndPoint(sock);
        ep->RecvHttpRequest();
        if (!ep->IsStop())
        {
            LOG(INFO, "recv sucess,begin handler request");
            ep->HandlerHttpRequest();
            ep->BuildHttpRequest();
            ep->SendHttpRequest();
        }
        else
        {
            LOG(WARNING, "recv error,stop handler request");
        }
        close(sock);
        delete ep;
        LOG(INFO, "handler request end");
    }
};